# -*- coding: utf-8 -*-

import struct, socket

class StructSocket():
	'''
	This module simpliefies binary access to sockets. It will buffer socket input automatically.
	
	You define the format of your data with StructSocket.add_struct. See struct.pack for format definition.
	
	Examples:
	
		StructSocket.add_struct("char", "c")
		StructSocket.get_char()                      # will return a char
		StructSocket.write_char("b")                 # will write a char
	
		StructSocket.add_struct("byte", "b")
		StructSocket.get_byte()                      # will return a byte
		StructSocket.write_byte(4)                   # will send a byte with value 4
	
		StructSocket.add_struct("myformat", "bhc")
		StructSocket.get_myformat()                  # will return a tuple containing a byte, a short and a char
		StructSocket.write_myformat(127, 32767, "b") # will send '\x7f\x00\xff\x7fb'
	
	'''
	
	prefix_r = "get_"
	prefix_w = "write_"
	
	def __init__(self, source):
		self.structs = {}
		self.source = source
		self.buffer = ""
	
	def add_struct(self, name, format):
		self.structs[name] = struct.Struct(format)
		
		if hasattr(self, self.prefix_r+name):
			delattr(self,  self.prefix_r+name)
		if hasattr(self, self.prefix_w+name):
			delattr(self,  self.prefix_w+name)
	
	def __getattr__(self, key):
		if key.startswith(self.prefix_r):
			name = key[len(self.prefix_r):]
			
			if name in self.structs:
				s = self.structs[name]
				
				def get():
					while len(self.buffer) < s.size:
						try:
							data = self.source.recv(4096)
						except socket.error:
							raise EOFError
						if data == "":
							raise EOFError
						self.buffer += data
					
					result = s.unpack_from(self.buffer)
					self.buffer = self.buffer[s.size:]
					
					if len(result) == 1:
						return result[0]
					return result
				
				setattr(self, self.prefix_r+name, get)
				return get
		
		if key.startswith(self.prefix_w):
			name = key[len(self.prefix_w):]
			
			if name in self.structs:
				s = self.structs[name]
				
				def write(*args):
					data = s.pack(*args)
					
					sent = 0
					while sent < len(data):
						sent += self.source.send(data[sent:])
					return sent
				
				setattr(self, self.prefix_w+name, write)
				return write
		
		return object.__getattribute__(self, key)
	

